import os
import time
from configparser import ConfigParser

import xlrd
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By


class SeleniumEdge:
    def __init__(self, driver):
        """初始化"""
        self.driver = driver
        self.vars = {}

    def close(self):
        """关闭"""
        self.driver.close()

    def wait_for_window(self, timeout=2):
        """等待页面打开"""
        time.sleep(round(timeout / 1000))
        wh_now = self.driver.window_handles
        wh_then = self.vars["window_handles"]
        if len(wh_now) > len(wh_then):
            return set(wh_now).difference(set(wh_then)).pop()

    def run(self, account, password, lesson_no, clazz_no, result_file):
        """编排脚本"""
        # 登录
        self.login(account, password)
        # 菜单
        self.menu()
        # 班级
        self.classes(lesson_no, clazz_no)
        # 成绩
        self.load_result(result_file)

    def load_result(self, excel_file):
        """录入成绩"""
        name_result_map = SeleniumEdge.read_excel_file(excel_file)
        # 24 | selectFrame | index = 0 |
        self.vars["win1701"] = self.wait_for_window()
        # 25 | click | id = CHKPSCJ2 |
        self.driver.switch_to.window(self.vars["win1701"])
        # 26 | click | id = CHKPSCJ1 |
        self.driver.switch_to.frame(0)
        # 27 | type | id = CHKPSCJ1 | 100
        trs = self.driver.find_elements(By.CSS_SELECTOR, "form[name='form1'] table tr.B,.H")
        for tr in trs:
            code = tr.find_element(By.CSS_SELECTOR, "td:nth-child(2)").text
            result = name_result_map.get(code)
            if not result:
                continue
            input0 = tr.find_elements(By.CSS_SELECTOR, "td.CJTD")[0].find_element(By.CSS_SELECTOR, "input")
            input2 = tr.find_elements(By.CSS_SELECTOR, "td.CJTD")[2].find_element(By.CSS_SELECTOR, "input")
            if not SeleniumEdge.equals_ignore_case("true", input0.get_attribute("readonly")):
                input0.send_keys(result[0])
            if not SeleniumEdge.equals_ignore_case("true", input2.get_attribute("readonly")):
                input2.send_keys(result[1])

    @staticmethod
    def equals_ignore_case(a, b):
        try:
            return a.lower() == b.lower()
        except AttributeError:
            return a == b

    @staticmethod
    def read_excel_file(excel_file):
        """
        读取excel重的成绩
        :rtype: dict
        """
        result = {}
        xl = xlrd.open_workbook(excel_file)
        sheet = xl.sheet_by_index(0)
        num = sheet.nrows
        for i in range(num):
            # 首行（即表头）不读取
            # TODO 跳过前4行
            if i == 0 or i == 1 or i == 2 or i == 3:
                continue
            row = sheet.row_values(i)
            # 学号
            # TODO 学号取第2列
            if isinstance(row[1], str):
                code = row[1]
            else:
                code = str(int(row[1]))
            # 平时成绩
            result1 = str(row[7])
            # 期末成绩
            result2 = str(row[12])
            result[code] = [result1, result2]
        return result

    def classes(self, lesson_no, clazz_no):
        """选择班级"""
        # 19 | selectFrame | index = 0 |
        self.driver.switch_to.frame(0)
        # 20 | click | id = button |
        self.driver.find_element(By.ID, "btn_retrieve").click()
        # 21 | selectFrame | index = 0 |
        self.driver.switch_to.frame(0)
        # 22 | click | css =.B: nth - child(1) >.AT |
        self.vars["window_handles"] = self.driver.window_handles
        # 23 | selectWindow | handle =${win1701} |
        trs = self.driver.find_elements(By.CSS_SELECTOR, ".B,.H")
        for tr in trs:
            tds = tr.find_elements(By.CSS_SELECTOR, "td")
            td0_text = tds[0].text
            td3_text = tds[3].text
            if lesson_no in td0_text and td3_text == clazz_no:
                tr.find_element(By.CSS_SELECTOR, ".AT").click()
                break

    def menu(self):
        """选择菜单"""
        # 可能会弹出“已在别处退出登录”的弹出框，点击确定
        try:
            self.driver.switch_to.alert.accept()
        except Exception as e:
            print(e)
        # 14 | selectFrame | relative = parent |
        self.driver.switch_to.default_content()
        # 15 | selectFrame | index = 2 |
        self.driver.switch_to.frame(2)
        # 16 | click | css =  # memuBarText5 > b |
        time.sleep(1)
        self.driver.find_element(By.CSS_SELECTOR, "#memuBarText5 > b").click()
        time.sleep(1)
        # 17 | click | css =  # memuLinkDiv5 > table > tbody > tr:nth-child(3) > td:nth-child(2) |
        self.driver.find_element(By.CSS_SELECTOR,
                                 "#memuLinkDiv5 > table > tbody > tr:nth-child(3) > td:nth-child(2)").click()
        # 18 | click | css =  # divGrpC0502 tr:nth-child(2) > td:nth-child(2) |
        self.driver.find_element(By.CSS_SELECTOR, "#divGrpC0502 tr:nth-child(2) > td:nth-child(2)").click()

    def login(self, account, password):
        """登录"""
        # 1 | open | /index.aspx |
        self.driver.get("http://jw.zfc.edu.cn/index.aspx")
        # 2 | setWindowSize | 1270x680 |
        self.driver.set_window_size(1270, 680)
        # 3 | selectFrame | index=0 |
        self.driver.switch_to.frame(0)
        # 4 | click | name=Sel_Type |
        self.driver.find_element(By.NAME, "Sel_Type").click()
        # 5 | select | name=Sel_Type | label=教师教辅人员
        dropdown = self.driver.find_element(By.NAME, "Sel_Type")
        dropdown.find_element(By.XPATH, "//option[. = '教师教辅人员']").click()
        # 6 | click | id=txt_asmcdefsddsd |
        self.driver.find_element(By.ID, "txt_asmcdefsddsd").click()
        # 7 | type | id=txt_asmcdefsddsd | 20210040
        self.driver.find_element(By.ID, "txt_asmcdefsddsd").send_keys(account)
        # 8 | click | css=tr:nth-child(2) > td:nth-child(2) |
        self.driver.find_element(By.CSS_SELECTOR, "tr:nth-child(2) > td:nth-child(2)").click()
        # 9 | mouseDown | id=txt_psasas |
        element = self.driver.find_element(By.ID, "txt_psasas")
        ActionChains(self.driver).move_to_element(element).click_and_hold().perform()
        # 10 | mouseUp | id=txt_pewerwedsdfsdff |
        element = self.driver.find_element(By.ID, "txt_pewerwedsdfsdff")
        ActionChains(self.driver).move_to_element(element).release().perform()
        # 11 | click | css=tr:nth-child(3) > td:nth-child(2) |
        self.driver.find_element(By.CSS_SELECTOR, "tr:nth-child(3) > td:nth-child(2)").click()
        # 12 | type | id=txt_pewerwedsdfsdff | ***
        self.driver.find_element(By.ID, "txt_pewerwedsdfsdff").send_keys(password)
        # 13 | click | id=btn_login |
        self.driver.find_element(By.ID, "btn_login").click()


if __name__ == "__main__":
    # 读取配置文件
    cp = ConfigParser()
    cp.read('config.cfg', encoding="utf-8")
    cp_edge_driver_path = cp.get('base', 'edgeDriverPath')
    cp_account = cp.get('base', 'account')
    cp_password = cp.get('base', 'password')
    cp_lesson_no = cp.get('base', 'lessonNo')
    cp_clazz_no = cp.get('base', 'clazzNo')
    cp_excel_path = cp.get('base', 'excelPath')
    # driver要在外部创建，否则内存清理时会被关联清理
    os.environ['webdriver.edge.driver'] = cp_edge_driver_path
    out_driver = webdriver.Edge()
    # 执行
    SeleniumEdge(out_driver).run(cp_account, cp_password, cp_lesson_no, cp_clazz_no, cp_excel_path)
    # 等待人工操作
    time.sleep(60*60*24)
